﻿using CSLua;
using Mono.Cecil;
using Mono.Cecil.Pdb;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace TK_CSLua.ILAnalyze
{
    // Token: 0x0200000A RID: 10
    public class LuaAssembly
    {
        // Token: 0x17000010 RID: 16
        // (get) Token: 0x06000042 RID: 66 RVA: 0x000034D0 File Offset: 0x000016D0
        public IEnumerable<LuaType> LuaTypes
        {
            get
            {
                return this._types;
            }
        }

        // Token: 0x17000011 RID: 17
        // (get) Token: 0x06000043 RID: 67 RVA: 0x000034D8 File Offset: 0x000016D8
        public AssemblyDefinition ilAssembly
        {
            get
            {
                return this._assembly;
            }
        }

        // Token: 0x17000012 RID: 18
        // (get) Token: 0x06000044 RID: 68 RVA: 0x000034E0 File Offset: 0x000016E0
        public IEnumerable<TypeDefinition> ProtoEnumTypes
        {
            get
            {
                return LuaAssembly._protoEnumTypes;
            }
        }

        // Token: 0x06000045 RID: 69 RVA: 0x000034E7 File Offset: 0x000016E7
        public LuaAssembly(string assemblyPath, List<string> onlyTypes = null)
        {
            this._assemblyPath = assemblyPath;
            this._onlyTypes = onlyTypes;
        }

        // Token: 0x06000046 RID: 70 RVA: 0x00003500 File Offset: 0x00001700
        public void DoAnalyze()
        {
            this._assembly = AssemblyDefinition.ReadAssembly(this._assemblyPath);
            this.analyzeAssemblyConfig();
            this._types = new List<LuaType>();
            string pdbName = Path.Combine(Path.GetDirectoryName(this._assemblyPath), Path.GetFileNameWithoutExtension(this._assemblyPath) + ".pdb");
            foreach (ModuleDefinition module in this._assembly.Modules)
            {
                if (File.Exists(pdbName))
                {
                    using (Stream s = File.OpenRead(pdbName))
                    {
                        module.ReadSymbols(new PdbReaderProvider().GetSymbolReader(module, s));
                    }
                }
                this.analyzeModule(module);
            }
        }

        // Token: 0x06000047 RID: 71 RVA: 0x000035DC File Offset: 0x000017DC
        private TypeDefinition GetTypeDefine(TypeReference typeRef)
        {
            if (typeRef.IsGenericInstance)
            {
                return ((GenericInstanceType)typeRef).GetElementType().Resolve();
            }
            return typeRef.Resolve();
        }

        // Token: 0x06000048 RID: 72 RVA: 0x00003600 File Offset: 0x00001800
        private void CollectConfigType(List<TypeDefinition> configTypes, TypeReference typeObj)
        {
            Stack<TypeDefinition> stackTemp = new Stack<TypeDefinition>();
            for (TypeReference baseType = typeObj; baseType != null; baseType = baseType.Resolve().BaseType)
            {
                if (baseType.isTypeOf<object>())
                {
                    break;
                }
                stackTemp.Push(baseType.Resolve());
            }
            while (stackTemp.Count > 0)
            {
                configTypes.Add(stackTemp.Pop());
            }
        }

        // Token: 0x06000049 RID: 73 RVA: 0x00003654 File Offset: 0x00001854
        private void analyzeAssemblyConfig()
        {
            List<TypeDefinition> configTypes = new List<TypeDefinition>();
            foreach (CustomAttribute attribute in this._assembly.CustomAttributes)
            {
                if (attribute.AttributeType.FullName == CSLStub.CSLConfigAttribute)
                {
                    TypeReference typeObj = (TypeReference)attribute.ConstructorArguments[0].Value;
                    this.CollectConfigType(configTypes, typeObj);
                }
            }
            this.renameTypes = new Dictionary<TypeDefinition, string>();
            this.rebindTypes = new Dictionary<TypeDefinition, TypeDefinition>();
            this.newValueFuncs = new Dictionary<string, bool>();
            this.immutableValueFuncs = new Dictionary<string, bool>();
            foreach (TypeDefinition typeDefinition in configTypes)
            {
                foreach (CustomAttribute attribute2 in typeDefinition.CustomAttributes)
                {
                    if (attribute2.AttributeType.FullName == CSLStub.RebindTypeAttribute)
                    {
                        TypeReference rawType = (TypeReference)attribute2.ConstructorArguments[0].Value;
                        TypeReference rebindType = (TypeReference)attribute2.ConstructorArguments[1].Value;
                        this.rebindTypes[this.GetTypeDefine(rawType)] = this.GetTypeDefine(rebindType);
                    }
                    else if (attribute2.AttributeType.FullName == CSLStub.RenameTypeAttribute)
                    {
                        TypeReference rawType2 = (TypeReference)attribute2.ConstructorArguments[0].Value;
                        string rename = (string)attribute2.ConstructorArguments[1].Value;
                        this.renameTypes[this.GetTypeDefine(rawType2)] = rename;
                    }
                    else if (attribute2.AttributeType.FullName == CSLStub.NewValueFuncAttribute)
                    {
                        string allocFuncName = (string)attribute2.ConstructorArguments[0].Value;
                        this.newValueFuncs[allocFuncName] = true;
                    }
                    else if (attribute2.AttributeType.FullName == CSLStub.ImmutableValueFuncAttribute)
                    {
                        string allocFuncName2 = (string)attribute2.ConstructorArguments[0].Value;
                        this.immutableValueFuncs[allocFuncName2] = true;
                    }
                }
            }
        }

        // Token: 0x0600004A RID: 74 RVA: 0x00003918 File Offset: 0x00001B18
        public string ResloveTypeName(TypeDefinition type)
        {
            string result = null;
            this.renameTypes.TryGetValue(type.Resolve(), out result);
            return result;
        }

        // Token: 0x0600004B RID: 75 RVA: 0x0000393C File Offset: 0x00001B3C
        internal MethodDefinition GetRebindMethod(MethodReference methodRef)
        {
            TypeDefinition result = null;
            if (!this.rebindTypes.TryGetValue(methodRef.DeclaringType.Resolve(), out result))
            {
                return null;
            }
            return result.Methods.FirstOrDefault((MethodDefinition m) => this.IsMethodSigEquals(m, methodRef));
        }

        // Token: 0x0600004C RID: 76 RVA: 0x00003998 File Offset: 0x00001B98
        internal bool IsMethodSigEquals(MethodReference method1, MethodReference method2)
        {
            if (method1.Name != method2.Name)
            {
                return false;
            }
            if (method1.Parameters.Count != method2.Parameters.Count)
            {
                return false;
            }
            for (int i = 0; i < method1.Parameters.Count; i++)
            {
                if (method1.Parameters[i].ParameterType.Resolve() != method2.Parameters[i].ParameterType.Resolve())
                {
                    return false;
                }
            }
            if (method1.ReturnType.Resolve() != method1.ReturnType.Resolve())
            {
                throw new Exception("返回值不一致：left:" + method1.ToString() + " right:" + method2.ToString());
            }
            return true;
        }

        // Token: 0x0600004D RID: 77 RVA: 0x00003A54 File Offset: 0x00001C54
        public TypeDefinition ResloveType(TypeReference type)
        {
            TypeDefinition result = null;
            if (this.rebindTypes.TryGetValue(type.Resolve(), out result))
            {
                return result;
            }
            return type.Resolve();
        }

        // Token: 0x0600004E RID: 78 RVA: 0x00003A80 File Offset: 0x00001C80
        private string GetMemberPath(MemberReference memberRef)
        {
            return memberRef.DeclaringType.FullName + "." + memberRef.Name;
        }

        // Token: 0x0600004F RID: 79 RVA: 0x00003AA0 File Offset: 0x00001CA0
        public bool IsNewValueFunc(MemberReference memberRef)
        {
            string path = this.GetMemberPath(memberRef);
            bool result = false;
            return this.newValueFuncs.TryGetValue(path, out result) && result;
        }

        // Token: 0x06000050 RID: 80 RVA: 0x00003ACC File Offset: 0x00001CCC
        public bool IsImmutableValueFunc(MemberReference memberRef)
        {
            string path = this.GetMemberPath(memberRef);
            bool result = false;
            return this.immutableValueFuncs.TryGetValue(path, out result) && result;
        }

        // Token: 0x06000051 RID: 81 RVA: 0x00003AF6 File Offset: 0x00001CF6
        public bool IsRebindType(TypeReference type)
        {
            return !type.IsGenericParameter && this.rebindTypes.ContainsKey(type.Resolve());
        }

        // Token: 0x06000052 RID: 82 RVA: 0x00003B13 File Offset: 0x00001D13
        internal bool isRenameType(TypeReference refe)
        {
            return this.renameTypes.ContainsKey(refe.Resolve());
        }

        // Token: 0x06000053 RID: 83 RVA: 0x00003B28 File Offset: 0x00001D28
        private void analyzeModule(ModuleDefinition moduleDef)
        {
            LuaAssembly._protoEnumTypes = new List<TypeDefinition>();
            foreach (TypeDefinition type in moduleDef.Types)
            {
                string luaModule = this.tryGetOutLuaModule(type);
                if (luaModule != null)
                {
                    LuaType luaType = new LuaType(this, luaModule, type);
                    luaType.DoAnalyze();
                    this._types.Add(luaType);
                    LuaAssembly.reslovedLuaTypes.Add(type, luaType);
                }
                if (type.hasAttributes(BuildConfig.protoAttribute))
                {
                    LuaAssembly._protoEnumTypes.Add(type);
                }
            }
        }

        // Token: 0x06000054 RID: 84 RVA: 0x00003BC8 File Offset: 0x00001DC8
        internal bool isForceUnvirtual(MethodReference methodRef)
        {
            if (methodRef.DeclaringType.Resolve().hasAttributes(BuildConfig.unvirtualAttribute))
            {
                return true;
            }
            MethodDefinition rebindMethod = this.GetRebindMethod(methodRef);
            if (rebindMethod != null)
            {
                if (rebindMethod.hasAttributes(BuildConfig.unvirtualAttribute))
                {
                    return true;
                }
                if (rebindMethod.DeclaringType.hasAttributes(BuildConfig.unvirtualAttribute))
                {
                    return true;
                }
            }
            return false;
        }

        // Token: 0x06000055 RID: 85 RVA: 0x00003C20 File Offset: 0x00001E20
        private string tryGetOutLuaModule(TypeDefinition mainTypeDef)
        {
            if (!AnalyzeUtils.checkBuildType(mainTypeDef))
            {
                return null;
            }
            if (this._onlyTypes != null && !this._onlyTypes.Contains(mainTypeDef.FullName))
            {
                return null;
            }
            string luaModule = mainTypeDef.Namespace.Replace('.', '/');
            CustomAttribute outAttr = mainTypeDef.getAttribute(BuildConfig.outAttribute);
            if (outAttr != null)
            {
                luaModule = (string)outAttr.ConstructorArguments.First<CustomAttributeArgument>().Value;
            }
            return luaModule;
        }

        // Token: 0x06000056 RID: 86 RVA: 0x00003C8D File Offset: 0x00001E8D
        public bool IsInternalType(TypeReference fieldType)
        {
            return fieldType.Resolve().Module.Assembly == this._assembly;
        }

        // Token: 0x04000023 RID: 35
        private AssemblyDefinition _assembly;

        // Token: 0x04000024 RID: 36
        private string _assemblyPath;

        // Token: 0x04000025 RID: 37
        private List<LuaType> _types;

        // Token: 0x04000026 RID: 38
        private List<string> _onlyTypes;

        // Token: 0x04000027 RID: 39
        public Dictionary<TypeDefinition, TypeDefinition> rebindTypes;

        // Token: 0x04000028 RID: 40
        public Dictionary<TypeDefinition, string> renameTypes;

        // Token: 0x04000029 RID: 41
        public Dictionary<string, bool> newValueFuncs;

        // Token: 0x0400002A RID: 42
        public Dictionary<string, bool> immutableValueFuncs;

        // Token: 0x0400002B RID: 43
        private static Dictionary<TypeDefinition, LuaType> reslovedLuaTypes = new Dictionary<TypeDefinition, LuaType>();

        // Token: 0x0400002C RID: 44
        private static List<TypeDefinition> _protoEnumTypes;
    }
}
